AIコーディング 2026.04.22

Claude CodeでMCPサーバー連携【2026年版】設定方法とPythonカスタムサーバー実装

タグ:Claude Code / MCP / Python / API連携 / 外部データ

MCPの仕組み

Claude Code ←→ MCPサーバー ←→ 外部API/DB/ファイル

MCPサーバーは「Claude用のプラグイン」。
ツールを定義すると、ClaudeがそのツールをAPIコールするように使えます。

設定ファイルの書き方

// ~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "exchange-rates": {
      "command": "python",
      "args": ["/Users/you/mcp-servers/exchange_server.py"],
      "env": {
        "EXCHANGE_API_KEY": "your-api-key-here"
      }
    },
    "database": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-sqlite", "/path/to/db.sqlite"]
    }
  }
}

設定後 claude --mcp-servers で認識されているサーバーを確認:

claude --mcp-servers
# → exchange-rates: connected
# → database: connected

Pythonで為替レートMCPサーバーを実装する

# exchange_server.py
# pip install mcp requests

import os
import requests
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp import types

app = Server("exchange-rates")
API_KEY = os.environ.get("EXCHANGE_API_KEY", "")

@app.list_tools()
async def list_tools() -> list[types.Tool]:
    """Claudeに提供するツールの一覧を返す"""
    return [
        types.Tool(
            name="get_exchange_rate",
            description="通貨間の為替レートを取得します",
            inputSchema={
                "type": "object",
                "properties": {
                    "from_currency": {
                        "type": "string",
                        "description": "変換元通貨(例: JPY, USD, EUR)"
                    },
                    "to_currency": {
                        "type": "string",
                        "description": "変換先通貨"
                    },
                    "amount": {
                        "type": "number",
                        "description": "変換する金額",
                        "default": 1
                    }
                },
                "required": ["from_currency", "to_currency"]
            }
        ),
        types.Tool(
            name="list_currencies",
            description="利用可能な通貨の一覧を返します",
            inputSchema={"type": "object", "properties": {}}
        )
    ]

@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
    """ツールを実行してClaudeに結果を返す"""
    
    if name == "get_exchange_rate":
        from_curr = arguments["from_currency"].upper()
        to_curr = arguments["to_currency"].upper()
        amount = arguments.get("amount", 1)
        
        # exchangerate-api.com(無料プランあり)を使用
        response = requests.get(
            f"https://v6.exchangerate-api.com/v6/{API_KEY}/pair/{from_curr}/{to_curr}/{amount}"
        )
        data = response.json()
        
        if data.get("result") == "success":
            rate = data["conversion_rate"]
            converted = data["conversion_result"]
            result = f"{amount} {from_curr} = {converted:.4f} {to_curr}(レート: {rate:.6f})"
        else:
            result = f"エラー: {data.get('error-type', '不明なエラー')}"
        
        return [types.TextContent(type="text", text=result)]
    
    if name == "list_currencies":
        currencies = ["JPY", "USD", "EUR", "GBP", "AUD", "CAD", "CHF", "CNY", "KRW"]
        return [types.TextContent(type="text", text=", ".join(currencies))]
    
    return [types.TextContent(type="text", text=f"不明なツール: {name}")]

async def main():
    async with stdio_server() as (read_stream, write_stream):
        await app.run(read_stream, write_stream, app.create_initialization_options())

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

Claude Codeから使う

# Claude Codeを起動
claude

# MCPツールが使えるようになる
> 100円は今何ドル?
# → Claudeがget_exchange_rateツールを呼び出して最新レートを取得して回答

> 「今日の為替レートを取得してCSVに保存するスクリプトを書いて」と指示すると
# → ClaudeがMCPツールでレートを取得し、CSVを生成するコードを書いてくれる

データベース連携のMCPサーバー

# database_server.py
# SQLiteデータベースに自然言語でクエリを実行する

import sqlite3
from mcp.server import Server
from mcp import types
from mcp.server.stdio import stdio_server

app = Server("database")
DB_PATH = "sales.db"

@app.list_tools()
async def list_tools() -> list[types.Tool]:
    return [
        types.Tool(
            name="query_db",
            description="SQLiteデータベースにSQLクエリを実行します",
            inputSchema={
                "type": "object",
                "properties": {
                    "sql": {"type": "string", "description": "実行するSQLクエリ(SELECT推奨)"},
                },
                "required": ["sql"]
            }
        ),
        types.Tool(
            name="list_tables",
            description="データベースのテーブル一覧を返します",
            inputSchema={"type": "object", "properties": {}}
        )
    ]

@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[types.TextContent]:
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    cursor = conn.cursor()
    
    try:
        if name == "query_db":
            sql = arguments["sql"]
            # SELECT以外は拒否(セキュリティ対策)
            if not sql.strip().upper().startswith("SELECT"):
                return [types.TextContent(type="text", text="エラー: SELECTクエリのみ許可されています")]
            
            cursor.execute(sql)
            rows = cursor.fetchall()
            
            if not rows:
                return [types.TextContent(type="text", text="結果なし")]
            
            # テーブル形式で返す
            headers = rows[0].keys()
            result = " | ".join(headers) + "\n"
            result += "-" * 40 + "\n"
            for row in rows[:20]:  # 最大20行
                result += " | ".join(str(v) for v in row) + "\n"
            
            return [types.TextContent(type="text", text=result)]
        
        if name == "list_tables":
            cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
            tables = [row[0] for row in cursor.fetchall()]
            return [types.TextContent(type="text", text=", ".join(tables))]
    
    finally:
        conn.close()
    
    return [types.TextContent(type="text", text="不明なツール")]

あわせて読みたい

参考ソース