MCPとは何か

MCP(Model Context Protocol)はAnthropicが2024年末に公開したオープンプロトコルで、AIアシスタントと外部ツール・データソースを標準化された方法で接続するための仕様です。

Claude Codeにはデフォルトでファイル読み書き・Bashコマンド実行などのツールが用意されていますが、MCPサーバーを追加することで独自のツールを組み込めます。たとえば社内APIへのアクセス・Notionのページ取得・Slackへのメッセージ送信といった機能を、自然言語でClaude Codeに指示するだけで実行できるようになります。

MCPサーバーの仕組み

MCPはクライアント(Claude Code)とサーバー(独自実装)間の通信プロトコルです。

通信はstdio(標準入出力)またはSSEで行われます。Claude CodeがMCPサーバーを子プロセスとして起動し、JSONRPCメッセージでやり取りします。

MCPサーバーが提供できるものは3種類です。

  • Tools:Claudeが実行できる関数(ファイル取得・API呼び出しなど)
  • Resources:Claudeが参照できるデータ(ドキュメント・DB内容など)
  • Prompts:定型プロンプトのテンプレート

今回は最も使われるToolsの実装に絞って説明します。

TypeScriptでのMCPサーバー実装

公式SDKを使って天気情報を返すシンプルなMCPサーバーを作ります。

mkdir my-mcp-server && cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk
npm install -D typescript @types/node ts-node
// src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({
  name: "weather-mcp",
  version: "1.0.0",
});

// ツールの定義
server.tool(
  "get_weather",
  "指定した都市の現在の天気を返す",
  {
    city: z.string().describe("都市名(例:東京、大阪)"),
  },
  async ({ city }) => {
    // 実際にはWeather APIを呼ぶ
    const weatherData = {
      city,
      temperature: 18,
      condition: "晴れ",
      humidity: 60,
    };

    return {
      content: [
        {
          type: "text",
          text: JSON.stringify(weatherData, null, 2),
        },
      ],
    };
  }
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("Weather MCP server started");
}

main().catch(console.error);
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "outDir": "./dist",
    "rootDir": "./src"
  }
}

PythonでのMCPサーバー実装

pip install mcp
# server.py
from mcp.server import Server
from mcp.server.stdio import stdio_server
from mcp.types import Tool, TextContent
import json

app = Server("weather-mcp")

@app.list_tools()
async def list_tools() -> list[Tool]:
    return [
        Tool(
            name="get_weather",
            description="指定した都市の現在の天気を返す",
            inputSchema={
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "都市名"
                    }
                },
                "required": ["city"]
            }
        )
    ]

@app.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
    if name == "get_weather":
        city = arguments["city"]
        # 実際にはAPIを呼ぶ
        data = {"city": city, "temperature": 18, "condition": "晴れ"}
        return [TextContent(type="text", text=json.dumps(data, ensure_ascii=False))]
    raise ValueError(f"Unknown tool: {name}")

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

Claude Codeへの登録

MCPサーバーをClaude Codeで使えるようにするには、設定ファイルに登録します。

~/.claude/settings.json または プロジェクトの .claude/settings.json を編集します。

{
  "mcpServers": {
    "weather": {
      "command": "node",
      "args": ["/absolute/path/to/my-mcp-server/dist/index.js"],
      "env": {
        "WEATHER_API_KEY": "your-api-key"
      }
    }
  }
}

Pythonの場合:

{
  "mcpServers": {
    "weather": {
      "command": "python",
      "args": ["/absolute/path/to/server.py"]
    }
  }
}

動作確認

Claude Codeを再起動すると、登録したMCPサーバーが自動的に読み込まれます。

claude

起動後に以下のように話しかけると、定義したツールが呼び出されます。

東京の今日の天気を調べてください

Claude Codeが get_weather ツールを選択して呼び出し、返ってきた結果を自然言語で回答します。

ツールが認識されているか確認するには /mcp コマンドを使います。

デバッグのコツ

MCPサーバーのデバッグはstderrへのログ出力が基本です(stdoutはJSONRPC通信に使われるため)。

TypeScriptなら console.error("debug:", data) 、Pythonなら import sys; print("debug:", data, file=sys.stderr) でデバッグ情報を出力できます。

claude --mcp-debug フラグで起動するとMCP通信の詳細ログが確認できます。

まとめ

MCPサーバーの自作はTypeScript・Pythonどちらでも公式SDKを使って比較的シンプルに実装できます。settings.json に登録するだけでClaude Codeがツールとして認識し、自然言語で呼び出せるようになります。社内APIや独自データソースへのアクセスをMCPサーバーとして実装することで、Claude Codeを自分のワークフローに統合できます。