2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MCPでDBを扱ってClaudeに数値計算させてみた!

Last updated at Posted at 2025-06-27

はじめに

こんにちは。株式会社船井総合研究所の平尾です。
今回は、前回の記事 MCPを使って機械学習してみた!の続きです。
前回はClaudeのチャット入力欄で数値計算に必要な情報を与え、MCPで計算させていましたが、今回はその情報をあらかじめDBに格納します。
ClaudeをMCPに繋いで、DBの情報をもとに数値計算させてみるというのが今回の内容です。
※scikit-learnのdiabetesデータを用いて作成した機械学習モデルを用いて計算します。適宜以下の記事を参考にしてください。
モデルを格納したpickleファイルの準備:MCPを使って機械学習してみた!
モデル構築の流れ:グリッドサーチとOptunaを実際に使って比較してみた!

環境を準備する

MCP実行環境の準備

terminal
# Pythonプロジェクト作成
uv init hellomcp
cd hellomcp

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

# MCPサーバーのPython SDKをインストール
uv add "mcp[cli]"

MCPサーバーのテストツール"MCP Inspector"の使い方より引用
uvのインストールに関しては、uvの使い方: Pythonパッケージ&プロジェクトマネージャーをご覧ください。

必要なライブラリのインポート

terminal
uv pip install pandas
uv pip install xgboost

DBを準備する

SQliteを用いてDBを準備します。
参考記事:python3でsqlite3の操作。作成や読み出しなどの基礎。

データベースを作成する

create_db.py
import sqlite3

# TEST.dbを作成する
# すでに存在していれば、それにアスセスする。
dbname = 'TEST.db'
conn = sqlite3.connect(dbname)

# データベースへのコネクションを閉じる。(必須)
conn.close()

テーブルを作成する

create_table1.py
import sqlite3

# TEST.dbを作成する
# すでに存在していれば、それにアクセスする。
dbname = 'TEST.db'
conn = sqlite3.connect(dbname)
# sqliteを操作するカーソルオブジェクトを作成
cur = conn.cursor()

# personsというtableを作成してみる
# 大文字部はSQL文。小文字でも問題ない。
cur.execute(
    'CREATE TABLE persons(' \
    'id INTEGER PRIMARY KEY AUTOINCREMENT,' \
    'name STRING,' \
    'age INTEGER,' \
    'sex STRING,' \
    'bmi REAL,' \
    'bp REAL,' \
    'tc REAL,' \
    'ldl REAL,' \
    'hdl REAL,' \
    'tch REAL,' \
    'ltg REAL,' \
    'glu REAL)')

# データベースへコミット。これで変更が反映される。
conn.commit()
conn.close()
create_table2.py
import sqlite3

dbname = 'TEST.db'
conn = sqlite3.connect(dbname)
cur = conn.cursor()

person_data = [
            ('Taro', 24, '男性', 20.0, 30.4, 100.0, 150.9, 90.3, 5.6, 3.2, 80.0),
            ('Hanako', 40, '女性', 30.0, 90.4, 200.0, 120.9, 78.3, 8.6, 2.2, 70.0),
            ('Bob', 30, '男性', 50.0, 40.4, 150.0, 130.9, 78.3, 9.6, 3.2, 100.0)
]
insert_sql = """
            INSERT INTO persons (name, age, sex, bmi, bp, tc, ldl, hdl, tch, ltg, glu)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """

cur.executemany(insert_sql, person_data)

conn.commit()

cur.close()
conn.close()

MCPサーバーの構築

mcp_sse.py
from mcp.server.fastmcp import FastMCP
import logging
import pickle
import sqlite3
import pandas as pd
from typing import List, Dict
import xgboost

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

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

@mcp.resource(uri="db://persons")
async def get_persons_resource() -> List[Dict]:
    dbname = "TEST.db"
    conn = sqlite3.connect(dbname)
    cur = conn.cursor()

    # dbをpandasで読み出す。
    df = pd.read_sql('SELECT * FROM persons', conn)
    dict1= df.to_dict(orient='records')
    cur.close()
    conn.close()
    return dict1

@mcp.tool()
async def calculate() -> List[Dict]:
    dict1 = await get_persons_resource() 
    df2 = pd.DataFrame(dict1)
    df3 = df2.drop(columns=['id', 'name'], errors='ignore')
    map = {'男性': 1, '女性': 2}
    df3['sex'] = df2['sex'].map(map)

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

    loaded_model = pickle.load(open("model.pickle", "rb"))
    prediction = loaded_model.predict(df3)

    #出力値の辞書の作成
    dict2 = dict(enumerate(prediction))
    df4 = df2['name']
    dict3 = df4.to_dict()
    output_data = {dict3[key]: dict2[key] for key in dict2}

    #入力値(説明変数)の辞書の作成
    dict4 = df2.to_dict()
    dict5 = {
        dict4['name'][i]: {
            key: dict4[key][i] for key in dict4
        }
        for i in dict4['id'].keys()
    }
    keys_to_remove = {'id', 'name'}
    input_data = {
        name: {
            k: v for k, v in inner_dict.items()
            if k not in keys_to_remove
        }
        for name, inner_dict in dict5.items()
    }

    #上で作成した2つの辞書(input_data, output_data)の統合
    merged_data = [
    {
        'name': name,                       
        'input': input_data[name],          
        'output': float(output_data[name])         
    }
    for name in input_data
    ]
    return merged_data
    
def main():
    mcp.run(transport='sse')

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

resourceでDBを呼び出し、toolでDBを用いて数値計算させます。
コーディングは以下の記事を参考にしています。
Part1 : Azure AI Foundry で MCPを使ってみた【深掘りと最新動向調査】
リモートで動作する MCP Server を実装し、Claude アプリから呼び出してみる

また、データの正規化に用いる平均値、標準偏差に関しては、Diabetes dataの「Proc Means and Proc Print Output」のデータを用いています。

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

MCPをClaudeと接続する。

それでは、先ほど作ったmcp_sse.pyを実行してみます。

terminal
python mcp_sse.py  

続いて、ClaudeをMCPに接続します。
接続方法に関しては、MCPを使って機械学習してみた!の「MCPサーバーをClaudeに接続」の「Claudeを開き~」をご確認ください。

Claudeに計算指示をする

チャットで「MCPを用いて糖尿病進行度を予測してください。」と指示をします。
すると以下の内容が返ってきます。
スクリーンショット 2025-06-27 143925.png

このように、計算を実行してくれました。

計算結果が正しいか確認する

Claudeが適当な数値を出力していないか確認するコードがこちらです。ここではMCPとしての機能は与えていません。

test.py
import pickle
import sqlite3
import pandas as pd
import xgboost
import pprint

dbname = "TEST.db"
conn = sqlite3.connect(dbname)
cur = conn.cursor()

df = pd.read_sql('SELECT * FROM persons', conn)
dict1= df.to_dict(orient='records')
cur.close()
conn.close()

df2 = pd.DataFrame(dict1)
df3 = df2.drop(columns=['id', 'name'], errors='ignore')
map = {'男性': 1, '女性': 2}
df3['sex'] = df2['sex'].map(map)

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

loaded_model = pickle.load(open("model.pickle", "rb"))
prediction = loaded_model.predict(df3)

#出力値の辞書の作成
dict2 = dict(enumerate(prediction))
df4 = df2['name']
dict3 = df4.to_dict()
output_data = {dict3[key]: dict2[key] for key in dict2}

#入力値(説明変数)の辞書の作成
dict4 = df2.to_dict()
dict5 = {
    dict4['name'][i]: {
        key: dict4[key][i] for key in dict4
    }
    for i in dict4['id'].keys()
}

keys_to_remove = {'id', 'name'}
input_data = {
    name: {
        k: v for k, v in inner_dict.items()
        if k not in keys_to_remove
    }
    for name, inner_dict in dict5.items()
}

#上で作成した2つの辞書(input_data, output_data)の統合
merged_data = [
    {
        'name': name,                       
        'input': input_data[name],          
        'output': float(output_data[name])     
    }
    for name in input_data
]
pprint.pprint(merged_data)

実行すると、以下の結果が出力されます。

[{'input': {'age': 24,
            'bmi': 20.0,
            'bp': 30.4,
            'glu': 80.0,
            'hdl': 90.3,
            'ldl': 150.9,
            'ltg': 3.2,
            'sex': '男性',
            'tc': 100.0,
            'tch': 5.6},
  'name': 'Taro',
  'output': 98.32352447509766},
 {'input': {'age': 40,
            'bmi': 30.0,
            'bp': 90.4,
            'glu': 70.0,
            'hdl': 78.3,
            'ldl': 120.9,
            'ltg': 2.2,
            'sex': '女性',
            'tc': 200.0,
            'tch': 8.6},
  'name': 'Hanako',
  'output': 143.7842254638672},
 {'input': {'age': 30,
            'bmi': 50.0,
            'bp': 40.4,
            'glu': 100.0,
            'hdl': 78.3,
            'ldl': 130.9,
            'ltg': 3.2,
            'sex': '男性',
            'tc': 150.0,
            'tch': 9.6},
  'name': 'Bob',
  'output': 158.55596923828125}]

計算結果のみならず、年齢、性別、BMIなどといった説明変数の値も、Claudeが出力したものと同じになっています。(Bobのtchをhba1cと認識しているのは間違いですが、数値に間違いはありません。)

Claudeに色々お願いしてみる

説明変数の値のみ表示させてみます。
スクリーンショット 2025-06-27 145326.png

目的変数の値のみ出力させてみます。
スクリーンショット 2025-06-27 145433.png

人物を指定してみます。
スクリーンショット 2025-06-27 145847.png

おわりに

Claudeに様々な指示をするためのポイントは、
MCPのtoolの設定において、説明変数も込みで出力させるようコーディングすることです。
そうすることで、Claudeが元データを認識してくれます。

以上です。
最後までお読みいただきありがとうございました!

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?