MCPサーバーの作り方: Model Context Protocolの詳細ガイド

Community Article Published April 8, 2025

近年、生成AIの技術が急速に発展する中で、AIとアプリケーションを接続するための新しいプロトコルとして「Model Context Protocol(MCP)」が注目を集めています。MCPはAIモデル(特に大規模言語モデル:LLM)とアプリケーションの間でコンテキストを提供する方法を標準化するためのプロトコルです。この記事では、MCPサーバーの作り方について詳しく解説し、その後、API開発ツールとして人気が高まっているApidog(Postmanの代替ツール)についてもご紹介します。

また、APIテストと管理のためのツールとして、PostmanからApidogへの移行を検討する理由や、Apidogの主要な機能と利点についても紹介しました。高度なAPIデバッグ、テスト、モック、ドキュメント作成などの機能を備えたApidogは、現代のAPI開発ワークフローを大幅に効率化する優れたツールです。

MCPサーバーを自分で実装し、Apidogを使ってAPIを効率的にテスト・管理することで、AI駆動型アプリケーションの開発がさらに加速するでしょう。

MCPとは?

Model Context Protocol(MCP)は、アプリケーションがLLMにコンテキストを提供する方法を標準化するためのプロトコルです。MCPは「AI アプリケーション用のUSB-Cポートのようなもの」とも表現され、外部ツールやサービスからコンテキストを取得し、LLMの能力を拡張するための標準的な方法を提供します。

MCPの主な特徴は以下のとおりです:

  1. 標準化されたインターフェース: さまざまなツールやサービスとLLMを接続するための統一された方法を提供します
  2. コンテキスト拡張: LLMが外部データソースから情報を取得できるようにします
  3. アクション実行: コードの実行やデータ保存などのアクションをLLMから実行できます
  4. サービス連携: Google CalendarやGitHubなどの外部サービスと連携できます

MCPのアーキテクチャは、以下の3つの主要コンポーネントで構成されています:

  • ホスト: ユーザーが操作するLLMアプリケーション(Claude for DesktopやClineなど)
  • MCPクライアント: ホストアプリケーション内でサーバーとの接続を確立するコンポーネント
  • MCPサーバー: クライアントにコンテキスト、ツール、プロンプトを提供するサービス

MCPサーバーの環境準備

それでは、実際にMCPサーバーを作成する方法を見ていきましょう。ここでは、Pythonを使用した天気情報を提供するMCPサーバーの実装例を紹介します。

必要なツールのインストール

まずはuvというPython用の高速パッケージマネージャをインストールします:

curl -LsSf <https://astral.sh/uv/install.sh> | sh

次に、Pythonプロジェクトを作成し、初期化します:

uv init weather
uv venv
source .venv/bin/activate

そして、必要なパッケージをインストールします:

uv add "mcp[cli]" httpx

MCPサーバーコードの実装

天気情報を提供するMCPサーバーを実装するために、以下のコードをweather.pyとして保存します:

from typing import Any
import httpx
from mcp.server.fastmcp import FastMCP
import mcp

# Initialize FastMCP server
server = mcp.FastMCP("weather")

# Constants
NWS_API_BASE = "<https://api.weather.gov>"
USER_AGENT = "weather-app/1.0"

async def make_nws_request(url: str) -> dict[str, Any] | None:
    """Make a request to the NWS API with proper error handling."""
    headers = {
        "User-Agent": USER_AGENT,
        "Accept": "application/geo+json"
    }

    async with httpx.AsyncClient() as client:
        try:
            response = await client.get(url, headers=headers, timeout=30.0)
            response.raise_for_status()
            return response.json()
        except Exception:
            return None

def format_alert(feature: dict) -> str:
    """Format an alert feature into a readable string."""
    props = feature["properties"]
    return f"""
Event: {props.get('event', 'Unknown')}
Area: {props.get('areaDesc', 'Unknown')}
Severity: {props.get('severity', 'Unknown')}
Description: {props.get('description', 'No description available')}
Instructions: {props.get('instruction', 'No specific instructions provided')}
"""

@mcp.tool(server)
async def get_alerts(state: str) -> str:
    """Get weather alerts for a US state.

    Args:
        state: Two-letter US state code (e.g. CA, NY)
    """
    url = f"{NWS_API_BASE}/alerts/active/area/{state}"
    data = await make_nws_request(url)

    if not data or "features" not in data:
        return "Unable to fetch alerts or no alerts found."

    if not data["features"]:
        return "No active alerts for this state."

    alerts = [format_alert(feature) for feature in data["features"]]
    return "\\n---\\n".join(alerts)

@mcp.tool(server)
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a location.

    Args:
        latitude: Latitude of the location
        longitude: Longitude of the location
    """
    # First get the forecast grid endpoint
    points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
    points_data = await make_nws_request(points_url)

    if not points_data:
        return "Unable to fetch forecast data for this location."

    # Get the forecast URL from the points response
    forecast_url = points_data["properties"]["forecast"]
    forecast_data = await make_nws_request(forecast_url)

    if not forecast_data:
        return "Unable to fetch detailed forecast."

    # Format the periods into a readable forecast
    periods = forecast_data["properties"]["periods"]
    forecasts = []
    for period in periods[:5]:  # Only show next 5 periods
        forecast = f"""
{period['name']}:
Temperature: {period['temperature']}°{period['temperatureUnit']}
Wind: {period['windSpeed']} {period['windDirection']}
Forecast: {period['detailedForecast']}
"""
        forecasts.append(forecast)

    return "\\n---\\n".join(forecasts)

if __name__ == "__main__":
    # Initialize and run the server
    server.run(transport='stdio')

コードの解説

このMCPサーバーコードでは、以下のような主要な機能を実装しています:

  1. サーバー初期化: mcp.FastMCP("weather")でMCPサーバーを初期化しています。
  2. 定数定義: 米国国立気象局(NWS)のAPIベースURLとユーザーエージェントを定義しています。
  3. APIリクエスト関数: make_nws_request関数を定義して、天気情報APIへのリクエストを処理します。
  4. ツール定義: @mcp.tool(server)デコレータを使用して、以下の2つのツールを定義しています:
    • get_alerts: 米国の州の気象警報を取得
    • get_forecast: 特定の緯度・経度の天気予報を取得

デコレータの役割

@mcp.tool(server)というデコレータは、定義された関数(get_alertsget_forecast)をMCPツールとして登録する役割を果たします。このデコレータを使用することで、次のようなことが自動的に行われます:

  1. 関数名の取得
  2. 関数の引数情報の解析
  3. 関数の戻り値の型情報の解析
  4. これらの情報を使ってMCPサーバーに関数を登録

これにより、LLMクライアントからこれらの関数を呼び出すことができるようになります。

TypeScriptでMCPサーバーを実装する

Pythonだけでなく、TypeScriptでもMCPサーバーを実装することができます。ここでは、シンプルなサイコロ機能を提供するMCPサーバーの実装例を紹介します。

プロジェクトのセットアップ

まず、新しいプロジェクトを作成し、必要なパッケージをインストールします:

mkdir mcp-dice-roller
cd mcp-dice-roller
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D @types/node typescript vitest

package.jsonを以下のように編集します:

{
  "name": "mcp-dice-roller",
  "version": "1.0.0",
  "main": "src/server.ts",
  "type": "module",
  "bin": {
    "diceRoller": "./build/index.js"
  },
  "scripts": {
    "build": "tsc && chmod 755 build/index.js",
    "test": "vitest"
  },
  "files": [
    "build"
  ]
}

tsconfig.jsonを作成します:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./build",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

サイコロツールの実装

src/index.tsを作成し、MCPサーバーを初期化します:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";

// サーバーインスタンスの作成
export const server = new McpServer({
  name: "DiceRoller",
  version: "0.1.0",
});

server.tool(
  "getDiceRoll", // ツールの名前
  "Roll a dice with a specified number of sides and return the result.", // ツールの説明
  // ツールの引数を定義するスキーマ
  { sides: z.number().min(1).describe("Number of sides on the die") },
  // ツールが呼び出されたときに実行される関数
  async ({ sides }) => {
    // 1から指定された面数までのランダムな整数を生成
    const roll = Math.floor(Math.random() * sides) + 1;

    return {
      content: [
        {
          type: "text",
          text: roll.toString(),
        },
      ],
    };
  }
);

import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  // 標準出力をするとサーバーのレスポンスとして解釈されてしまうので、標準エラー出力に出力する
  console.error("MCP Server running on stdio");
}

main().catch((error) => {
  console.error("Fatal error in main():", error);
  process.exit(1);
});

サーバーのテスト

TypeScriptで実装したMCPサーバーをテストするために、src/index.test.tsを作成します:

import { describe, it, expect } from "vitest";
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
import { server } from "./index.js";

describe("getDiceRoll", () => {
  it("ランダムにサイコロを振った結果を返す", async () => {
    // テスト用クライアントの作成
    const client = new Client({
      name: "test client",
      version: "0.1.0",
    });

    // インメモリ通信チャネルの作成
    const [clientTransport, serverTransport] =
      InMemoryTransport.createLinkedPair();

    // クライアントとサーバーを接続
    await Promise.all([
      client.connect(clientTransport),
      server.connect(serverTransport),
    ]);

    // 6面サイコロを振る
    const result = await client.callTool({
      name: "getDiceRoll",
      arguments: {
        sides: 6,
      },
    });

    // 結果が1-6の範囲の数字であることを確認
    expect(result).toEqual({
      content: [
        {
          type: "text",
          text: expect.stringMatching(/^[1-6]$/),
        },
      ],
    });
  });
});

Claude for Desktopとの連携

作成したMCPサーバーをClaude for Desktopで使用するには、設定ファイルを編集する必要があります。

  1. Claude for Desktopをインストール(https://claude.ai/download)
  2. メニューバーの「Claude」→「Settings...」を選択
  3. 左側のメニューから「Developer」を選択
  4. 「Edit Config」ボタンをクリック
  5. claude_desktop_config.jsonファイルを編集:
{
  "mcpServers": {
    "diceRoller": {
      "command": "node",
      "args": [
        "/absolute/path/to/your/mcp-dice-roller/build/index.js"
      ]
    }
  }
}
  1. Claude for Desktopを再起動

これで、Claude for Desktopから「getDiceRoll」ツールを使用できるようになります。例えば、「1〜10のうちランダムな1つの数字を返してください」と質問すると、サーバーが実行され結果が返されます。

Apidog: 最高のPostman代替ツール

MCPサーバーの開発や利用には、APIのテストや管理が欠かせません。ここで、Postmanの代替として急速に人気を集めているAPIツール「Apidog」について紹介します。

Apidogとは

Apidogは、API開発プロセスを効率化するための統合ツールです。API設計、テスト、デバッグ、モック、ドキュメント作成まで、API開発に必要な機能を1つのプラットフォームに統合しています。Postmanのような既存のAPIツールよりも多くの機能を持ち、より効率的なワークフローを提供します。

Apidogの主要機能

  1. APIデバッグ: 視覚的で直感的なインターフェースを通じて、APIをデバッグできます。APIリクエスト履歴、変数検査、リアルタイムリクエストモニタリングなどの機能を提供し、問題を素早く特定して解決できます。
  2. 高度な変数サポート: グローバル、環境、ローカルの変数を作成・管理し、異なるシナリオでのAPIテストを簡素化します。APIキー、ユーザー認証情報、標準リクエストパラメータなどのデータを安全かつ効率的に保存できます。
  3. プリプロセッサ機能: 各APIリクエストの前にカスタムスクリプトを実行できます。特定の条件を設定したり、リクエストパラメータを動的に変更したりするのに役立ちます。
  4. APIモック: バックエンドAPIが完成する前に、フロントエンド開発を始めることができます。Faker.jsを使用した動的モックデータ生成をサポートし、リアルなAPIレスポンスを作成できます。
  5. APIテスト機能:
    • 視覚的アサーション: コードを書かずにAPIテストにアサーションを追加できます。
    • テストシナリオ: 複数のAPIケースを単一のテストシナリオにバンドルし、ワンクリックで実行できます。
    • データ駆動テスト: 異なるデータセットでAPIをテストし、一貫したパフォーマンスを確保できます。
    • 動的値: APIリクエストに動的パラメータを作成し、ランダムまたは常に変化するデータでテストできます。
    • CI/CD統合: APIテストをデプロイメントプロセスの一部として実行できます。
  6. APIドキュメント: 動的で使いやすいAPIドキュメントを生成できます。開発者はドキュメントから直接APIを試すことができ、APIの使用方法を理解しやすくなります。

PostmanとApidogの比較

機能 Postman Apidog
APIリポジトリ 中央集中型で管理可能 より高度な組織化と管理機能
大規模リクエスト処理 1000件以上のリクエスト処理に制限あり 大規模なリクエスト処理に対応
コード重複 動的APIリクエストのコード重複あり インテリジェントなワークスペース管理でコード重複を削減
ワークスペース管理 大規模プロジェクトでの管理が複雑 直感的で効率的な管理インターフェース
APIドキュメント 基本的な機能あり 美しく、インタラクティブなドキュメント生成
テスト機能 基本的なテスト機能あり 高度なアサーション、シナリオ、データ駆動テスト

Apidogの使い方

Apidogを使ってAPIをテストする基本的な手順は以下の通りです:

  1. Apidogアカウントの作成: Apidogのウェブサイトで新しいアカウントを作成するか、GoogleやGithubでログインします。

  2. ワークスペースのセットアップ: ログイン後、Apidogダッシュボードでワークスペースを作成し、APIテストを組織化します。

  3. 新しいプロジェクトの作成: ワークスペース内で、特定のアプリケーションやサービスに関連するAPIテストをグループ化するプロジェクトを作成します。

  4. APIの追加: 「Add API」ボタンを使用して、エンドポイント、メソッド、認証情報などのAPI詳細を入力します。

    URL: <http://localhost:5000/books>
    Method: GET
    Endpoint name: Get all books
    
  5. テストシナリオの設計: ApidogのビジュアルAPIビルダーを使用して、コーディングなしでテストシナリオとアサーションをデザインします。

  6. テストの実行: テストを個別またはグループで実行し、APIの機能性、信頼性、セキュリティを評価します。

  7. テスト結果の分析と最適化: Apidogの詳細なレポートと分析を活用して、テスト結果を分析し、APIを最適化します。

PostmanからApidogへの移行

既存のPostmanコレクションをApidogに移行するのは簡単です。Postmanコレクションをエクスポートし、ApidogのUI上でインポートするだけです。Apidogはスムーズな移行プロセスを提供し、すぐに新しいプラットフォームで作業を開始できます。

まとめ

本記事では、MCPサーバーの作り方について詳しく解説しました。Model Context Protocol(MCP)は、LLMとアプリケーションのコミュニケーションを標準化する重要なプロトコルであり、今後のAI開発において不可欠なコンポーネントになるでしょう。

Python版のMCPサーバーでは天気情報APIを活用したツールを、TypeScript版では簡単なサイコロツールの実装例を紹介しました。いずれも、LLMに外部のデータやサービスへのアクセスを提供する基本的な方法を示しています。

Community

Your need to confirm your account before you can post a new comment.

Sign up or log in to comment