メインコンテンツへスキップ
エンジニアリングMar 28, 2026

初めてのMCPサーバー構築:開発者向け実践ガイド

OS
Open Soft Team

Engineering Team

Model Context Protocol(MCP)とは?

Model Context Protocol(MCP) は、Anthropicが開発したオープンスタンダードであり、AIアプリケーションが外部データソースやツールと通信する方法を定義します。MCPは AI統合のためのUSB-C と考えてください。USB-Cが充電、データ転送、ディスプレイ出力のための単一のユニバーサルコネクタを提供するように、MCPはAIモデルをデータベース、API、ファイルシステム、その他のサービスに接続するための単一のユニバーサルプロトコルを提供します。MCPにより、すべてのAIアプリケーションとすべてのツール間のカスタム統合が不要になります。

2026年現在、MCPはAIツール通信の主要なスタンダードとなっています。Gartnerは 2026年末までにエンタープライズアプリケーションの40%がAIエージェントを含む と予測しており、MCPはこれを大規模に実現可能にするプロトコルです。MCP以前は、AIモデルをN個のツールに接続するにはN個のカスタム統合が必要でした。M個のAIアプリケーションとN個のツールでは、M x N個の統合アダプタが必要でした。MCPはこれをM + Nに削減します。すべてのAIアプリが1つのMCP clientを実装し、すべてのツールが1つのMCP serverを実装します。

MCPアーキテクチャ:Host、Client、Server、Transport

コードを書く前にMCPアーキテクチャを理解することが重要です。プロトコルは4つの主要な役割を定義しています:

コンポーネント役割
Hostエンドユーザーが操作するAIアプリケーションClaude Desktop、Cursor、VS Code Copilot
ClientHost内のMCPプロトコルハンドラーHostアプリケーションに組み込み
ServerMCPを通じてtools、resources、promptsを公開カスタムサーバー(これから構築するもの)
TransportClientとServer間の通信レイヤーstdio、HTTP+SSE、Streamable HTTP

通信フローは次のように動作します:

  1. Hostが起動し、設定された各サーバーに対してMCP Clientを初期化します。
  2. ClientTransport(ローカルはstdio、リモートはHTTP)を介してServerに接続します。
  3. Clientinitializeリクエストを送信し、プロトコルバージョンと機能をネゴシエートします。
  4. Serverが利用可能なtoolsresourcespromptsを応答します。
  5. AIモデルが外部データやアクションを必要とする場合、Clientが適切なサーバーメソッドを呼び出します。
  6. Serverが操作を実行し、JSON-RPC 2.0メッセージで結果を返します。

Tools vs Resources vs Prompts

MCP serverは3種類の機能を公開できます:

  • Tools はAIモデルが呼び出せる関数です。構造化された入力を受け取り、構造化された出力を返します。例:query_database(sql: string)send_email(to: string, subject: string, body: string)
  • Resources はAIモデルが読み取れるデータです。URIで識別され、コンテンツを返します。例:file:///path/to/document.mdpostgres://localhost/mydb/users
  • Prompts はサーバーが提供する再利用可能なプロンプトテンプレートです。AIがサーバーのドメインとやり取りする方法を標準化します。例:Jira MCP serverのsummarize_ticketプロンプトテンプレート。

ステップバイステップ:TypeScriptでMCPサーバーを構築

SQLiteデータベースにクエリを実行するツールを提供する実用的なMCPサーバーを構築しましょう。これは一般的なユースケースで、AIモデルにアプリケーションデータへの安全な読み取り専用アクセスを提供します。

ステップ1:プロジェクトの初期化

mkdir mcp-sqlite-server && cd mcp-sqlite-server
npm init -y
npm install @modelcontextprotocol/sdk better-sqlite3
npm install -D typescript @types/node @types/better-sqlite3
npx tsc --init

tsconfig.jsonを設定:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "Node16",
    "moduleResolution": "Node16",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "declaration": true
  },
  "include": ["src/**/*"]
}

ステップ2:サーバーの実装

src/index.tsを作成:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import Database from "better-sqlite3";
import { z } from "zod";

const db = new Database(process.env.DB_PATH || "./data.db", {
  readonly: true,
});

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

server.tool(
  "query",
  "SQLiteデータベースに対して読み取り専用SQLクエリを実行",
  {
    sql: z.string().describe("実行するSQL SELECTクエリ"),
  },
  async ({ sql }) => {
    const normalized = sql.trim().toUpperCase();
    if (!normalized.startsWith("SELECT")) {
      return {
        content: [{ type: "text", text: "エラー:SELECTクエリのみ許可されています。" }],
        isError: true,
      };
    }
    try {
      const rows = db.prepare(sql).all();
      return {
        content: [{ type: "text", text: JSON.stringify(rows, null, 2) }],
      };
    } catch (error) {
      return {
        content: [{ type: "text", text: `クエリエラー:${(error as Error).message}` }],
        isError: true,
      };
    }
  }
);

server.tool(
  "list_tables",
  "データベース内のすべてのテーブルとスキーマを一覧表示",
  {},
  async () => {
    const tables = db
      .prepare(
        `SELECT name, sql FROM sqlite_master
         WHERE type='table' AND name NOT LIKE 'sqlite_%'
         ORDER BY name`
      )
      .all();
    return {
      content: [{ type: "text", text: JSON.stringify(tables, null, 2) }],
    };
  }
);

ステップ3:ビルドとローカルテスト

npx tsc
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | node dist/index.js

サーバーの機能を含むJSON-RPCレスポンスが表示されるはずです。

ステップ4:Pythonで同じサーバーを構築

Python開発者向けに、公式MCP Python SDKを使用した同等の実装を示します:

import sqlite3
import json
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("sqlite-query")
DB_PATH = "data.db"

@mcp.tool()
def query(sql: str) -> str:
    """SQLiteデータベースに対して読み取り専用SQLクエリを実行。"""
    normalized = sql.strip().upper()
    if not normalized.startswith("SELECT"):
        raise ValueError("SELECTクエリのみ許可されています。")
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    try:
        cursor = conn.execute(sql)
        rows = [dict(row) for row in cursor.fetchall()]
        return json.dumps(rows, indent=2, default=str)
    finally:
        conn.close()

@mcp.tool()
def list_tables() -> str:
    """データベース内のすべてのテーブルとスキーマを一覧表示。"""
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    try:
        cursor = conn.execute(
            "SELECT name, sql FROM sqlite_master "
            "WHERE type='table' AND name NOT LIKE 'sqlite_%'"
        )
        tables = [dict(row) for row in cursor.fetchall()]
        return json.dumps(tables, indent=2)
    finally:
        conn.close()

if __name__ == "__main__":
    mcp.run(transport="stdio")

Claude Desktopへの接続

Claude DesktopはMCPサーバーをネイティブにサポートしています。サーバーを接続するには、Claude Desktop設定ファイルを編集します:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "sqlite-query": {
      "command": "node",
      "args": ["/absolute/path/to/dist/index.js"],
      "env": {
        "DB_PATH": "/absolute/path/to/your/data.db"
      }
    }
  }
}

Claude Desktopを再起動します。チャットインターフェースにハンマーアイコンが表示され、利用可能なMCPツールが示されます。「データベースにはどんなテーブルがありますか?」や「登録日順にトップ10のユーザーを表示して」のようにClaudeに質問すると、MCPサーバーを使用してデータベースに直接クエリを実行します。

Cursorへの接続

CursorもMCPサーバーをサポートしています。プロジェクトルートの.cursor/mcp.jsonに設定を追加します:

{
  "mcpServers": {
    "sqlite-query": {
      "command": "node",
      "args": ["./dist/index.js"],
      "env": {
        "DB_PATH": "./data.db"
      }
    }
  }
}

保存後、Cursorを再起動します。MCPツールがAIアシスタントパネルに表示され、コード生成やデバッグセッション中に呼び出すことができます。

MCPサーバーのテストとデバッグ

MCP Inspectorの使用

MCP Inspectorは公式デバッグツールです。サーバーとやり取りするためのWebインターフェースを提供します:

npx @modelcontextprotocol/inspector node dist/index.js

http://localhost:5173でブラウザインターフェースが開き、以下のことができます:

  • 登録されたすべてのtools、resources、promptsを表示
  • カスタム入力でツールを呼び出し、レスポンスを検査
  • JSON-RPCメッセージストリームをリアルタイムで監視
  • 不正なリクエストを送信してエラー処理をテスト

一般的なデバッグの問題

問題原因解決策
サーバーがClaude Desktopに表示されない設定パスまたはJSON構文エラーJSONを検証し、絶対パスを確認
「Tool not found」エラー接続前にツールが登録されていないserver.connect()を呼ぶ前にツールを登録
ツール呼び出しのタイムアウト進捗なしの長時間実行操作server.sendProgress()で進捗通知を追加
stderrの出力がプロトコルを破損console.logがstdoutに書き込み(stdioトランスポート)stdioではロギングにconsole.error()を使用
アイドル後の接続切断トランスポートのタイムアウトハートビートの実装またはHTTPトランスポートを使用

本番環境のベストプラクティス

  1. 入力バリデーション:常にツールの入力を検証しサニタイズします。TypeScriptではZodスキーマ、PythonではPydanticモデルを使用して厳密な型チェックを行います。

  2. デフォルトで読み取り専用:読み取り専用アクセスから始めます。書き込み機能は本当に必要な場合にのみ追加し、破壊的な操作には必ず確認を求めます。

  3. エラー処理isError: trueで構造化されたエラーメッセージを返します。内部スタックトレースやデータベース接続文字列を公開しないでください。

  4. ロギング:すべてのツール呼び出しをタイムスタンプ、入力、実行時間とともにログに記録します。stdioトランスポート使用時はログにstderr(stdoutではなく)を使用します。

  5. レート制限:制御されないAIループがバックエンドサービスを過負荷にすることを防ぐため、ツールごとのレート制限を実装します。

  6. タイムアウト:すべてのツールハンドラーに実行タイムアウトを設定します。AIモデルが高コストなクエリをトリガーするツールを呼び出す可能性があります。インフラストラクチャを保護しましょう。

  7. 環境の分離:すべての設定に環境変数を使用します。データベースURL、APIキー、ファイルパスをハードコードしないでください。

  8. バージョニング:MCPサーバーにセマンティックバージョニングを使用します。initializeハンドシェイクにはバージョンネゴシエーションが含まれ、破壊的変更にはメジャーバージョンのバンプが必要です。

FAQ

Q:MCPとfunction callingの違いは何ですか? A:Function calling(OpenAI、Anthropicなどが使用)は、各APIリクエストにインラインでツールを定義します。MCPはツール定義をスタンドアロンサーバーに外部化し、MCP互換ホストが発見して使用できるようにします。Function callingはリクエストごと、MCPはステートフルなセッションを持つ永続的なプロトコルです。

Q:Claude以外のモデルでMCPを使用できますか? A:はい。MCPはオープンプロトコルです。OpenAI、Google DeepMind、Microsoftは2026年初頭からプラットフォームにMCPサポートを採用しています。MCP clientを実装するすべてのAIアプリケーションは、あらゆるMCP serverに接続できます。

Q:MCPはローカルツール専用ですか? A:いいえ。stdioトランスポートはローカルサーバー向けに設計されていますが、HTTP+SSEおよびStreamable HTTPトランスポートはリモートMCPサーバーをサポートします。MCPサーバーをネットワーク経由でアクセス可能なクラウドサービスとしてデプロイできます。

Q:MCPは認証をどのように処理しますか? A:リモートサーバーにはOAuth 2.0をサポートします。ローカルstdioサーバーはホストプロセスのセキュリティコンテキストを継承します。エンタープライズデプロイメントでは、MCPゲートウェイが認証と認可を一元化できます。

Q:MCPサーバーを構築するにはどの言語を使用できますか? A:公式SDKはTypeScript、Python、Java、Kotlin、C#、Swiftで利用できます。コミュニティSDKにはRust、Go、Ruby、PHPが含まれます。プロトコルは言語に依存しません。stdioまたはHTTPを介してJSON-RPCの読み書きができる言語であれば、MCPサーバーを実装できます。

Q:ホストを再起動せずにMCPサーバーを更新する方法は? A:MCPは機能変更通知をサポートしています。サーバーのツールが変更された場合、サーバーはnotifications/tools/list_changedメッセージを送信し、clientにツールリストの再取得を促します。stdioサーバーの場合、通常ホストの再起動が必要です。HTTPサーバーはホストの再起動なしで更新できます。