🏠 ホーム ニュース 📖 解説記事 📚 トピック解説 🏷️ タグ一覧 ℹ️ About
🔍 記事を検索
カテゴリ
📡 RSSフィード
Follow
X (Twitter) 🧵 Threads
Quick Links
ニュース一覧 🏷️タグから探す
🧠Claude 🤖Agent 💬LLM 🔌MCP 🛠️Tool
Subscribe
📡 RSSフィード
ホーム explain 2026.04.07

MCPサーバーの作り方2026年完全ガイド:TypeScript・Python両対応チュートリアル

modelcontextprotocol/servers
🔧
MCPサーバーの作り方2026年完全ガイド:TypeScript・Python両対応チュートリアル - AIツール日本語解説 | AI Heartland
// なぜ使えるか
Claude CodeやCursorなどAIツールの拡張に不可欠なMCPサーバー。公式SDKの使い方から実践的なサーバー構築まで、日本語の包括チュートリアルがほぼ存在しない。TypeScript・Python両対応で解説する。

MCPサーバーとは何か:AIツール拡張の新標準プロトコル

2024年11月にAnthropicが公開したModel Context Protocol(MCP)は、AIアシスタントと外部ツールを接続するオープンな標準プロトコルだ。2026年4月時点で、Claude Code、Cursor、Cline、Windsurf、VS Code Copilotなど主要なAI開発ツールがMCPクライアントとして対応し、公式リポジトリ modelcontextprotocol/servers にはGitHub Star 83,000超が集まっている。

MCPが解決する問題はシンプルだ。従来、AIツールに「ファイルを読む」「データベースを検索する」「APIを叩く」といった機能を追加するには、ツールごとに独自のプラグイン仕様を学ぶ必要があった。MCPは1つのプロトコルで複数のAIクライアントに対応できるため、一度サーバーを作れば Claude Code でも Cursor でも使い回せる。

MCPの通信構造: MCPクライアント(Claude Code, Cursor等)がJSON-RPC over stdio/SSEでMCPサーバー(自作ツール)と通信し、MCPサーバーが任意のプロトコルで外部リソース(DB, API, ファイル等)にアクセスする。

MCPサーバーが提供できる機能は3種類ある。

機能 説明 具体例
Tools(ツール) AIが呼び出せる関数 ファイル書き込み、DB検索、API呼び出し
Resources(リソース) AIが読み取れるデータ 設定ファイル、ログ、ドキュメント
Prompts(プロンプト) 再利用可能なプロンプトテンプレート コードレビュー用プロンプト、SQL生成テンプレート

この記事では、TypeScriptとPythonの両方の公式SDKを使って、実用的なMCPサーバーをゼロからステップバイステップで構築する方法を解説する。

graph LR A["AIクライアント
Claude Code / Cursor"] -->|"JSON-RPC"| B["MCPサーバー
自作ツール"] B -->|"読み取り"| C["ファイルシステム"] B -->|"クエリ"| D["データベース"] B -->|"HTTP"| E["外部API"] B -->|"ツール定義"| A style A fill:#4A90D9,color:#fff style B fill:#7B68EE,color:#fff style C fill:#50C878,color:#fff style D fill:#50C878,color:#fff style E fill:#50C878,color:#fff

MCPサーバー構築デモ — セットアップからClaude Code接続まで

MCPサーバー開発環境のセットアップ手順

TypeScript SDK(推奨)

TypeScript SDKは最も成熟しており、公式サーバーの大半がTypeScriptで書かれている。

# プロジェクト作成
mkdir my-mcp-server && cd my-mcp-server
npm init -y

# MCP SDK と 型定義をインストール
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node

# TypeScript設定
npx tsc --init --target ES2022 --module NodeNext --moduleResolution NodeNext --outDir build --rootDir src --strict true
mkdir src

package.json に以下を追加する。

{
  "name": "my-mcp-server",
  "version": "1.0.0",
  "type": "module",
  "bin": {
    "my-mcp-server": "./build/index.js"
  },
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch"
  }
}

Python SDK

Python SDKは mcp パッケージとして提供されている。FastMCPという高レベルAPIが用意されており、デコレータベースで直感的にサーバーを定義できる。

# uvを使う場合(推奨)
uv init my-mcp-server && cd my-mcp-server
uv add "mcp[cli]"

# pipを使う場合
pip install "mcp[cli]"

Python SDKの FastMCP は、Flaskのようなデコレータスタイルでツールを定義できるのが特徴だ。

from mcp.server.fastmcp import FastMCP

# サーバーインスタンス作成
mcp = FastMCP("my-server")

# ツール定義(デコレータで宣言)
@mcp.tool()
def hello(name: str) -> str:
    """挨拶を返すツール"""
    return f"こんにちは、{name}さん!"

# サーバー起動
if __name__ == "__main__":
    mcp.run()

公式SDKの全体像(9言語対応)

MCPは2026年4月時点で9言語の公式SDKが提供されている。

SDK Tier GitHub Stars 推奨用途
TypeScript Tier 1 12,100+ 公式サーバーの主要言語、本番運用
Python Tier 1 22,500+ FastMCPで高速開発、データ分析連携
C# Tier 1 .NETエンタープライズ環境
Go Tier 1 高速・軽量サーバー
Java Tier 2 Spring Boot連携
Rust Tier 2 高パフォーマンス要件
Swift Tier 3 macOS/iOSネイティブ
Ruby Tier 3 Railsアプリ連携
PHP Tier 3 WordPress/Laravel連携

Tier 1は公式が完全サポート、Tier 2はコミュニティ主導+公式レビュー、Tier 3はコミュニティ主導。

TypeScript vs Python SDKの選択基準

基準 TypeScript SDK Python SDK (FastMCP)
成熟度 ★★★★★ 公式サーバーの主要言語 ★★★★★ Star 22,500+で最も人気
学習コスト 中(Zodスキーマ定義が必要) 低(デコレータのみ)
型安全性 完全な型推論 Pydantic連携で型検証
デプロイ npx で即実行可能 uv run または python -m
向いている用途 大規模・本番運用 プロトタイプ・データ分析連携

実践1:ファイル操作MCPサーバーをTypeScriptで構築する

最初のサーバーとして、指定ディレクトリ内のファイルを検索・読み取り・要約できるツールを作る。AIアシスタントが「プロジェクト内のREADMEを読んで」と言われたときに、このサーバーを通じてファイルにアクセスする。

サーバー本体の実装

src/index.ts を作成する。

#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import * as fs from "fs/promises";
import * as path from "path";

// サーバーインスタンス作成
const server = new McpServer({
  name: "file-explorer",
  version: "1.0.0",
});

// ルートディレクトリ(環境変数 or デフォルト)
const ROOT_DIR = process.env.MCP_ROOT_DIR || process.cwd();

// ツール1: ファイル一覧取得
server.tool(
  "list_files",
  "指定パターンにマッチするファイルの一覧を返す",
  {
    directory: z.string().describe("検索するディレクトリの相対パス"),
    extension: z.string().optional().describe("フィルタする拡張子(例: .md, .ts)"),
  },
  async ({ directory, extension }) => {
    const targetDir = path.resolve(ROOT_DIR, directory);
    
    // ディレクトリトラバーサル防止
    if (!targetDir.startsWith(ROOT_DIR)) {
      return { content: [{ type: "text", text: "エラー: ルートディレクトリ外へのアクセスは禁止" }] };
    }
    
    const entries = await fs.readdir(targetDir, { withFileTypes: true, recursive: true });
    let files = entries
      .filter(e => e.isFile())
      .map(e => path.relative(ROOT_DIR, path.join(e.parentPath || targetDir, e.name)));
    
    if (extension) {
      files = files.filter(f => f.endsWith(extension));
    }
    
    return {
      content: [{ type: "text", text: files.join("\n") || "ファイルが見つかりません" }],
    };
  }
);

// ツール2: ファイル読み取り
server.tool(
  "read_file",
  "指定パスのファイル内容を返す",
  {
    file_path: z.string().describe("読み取るファイルの相対パス"),
    max_lines: z.number().optional().default(200).describe("最大行数(デフォルト200)"),
  },
  async ({ file_path, max_lines }) => {
    const fullPath = path.resolve(ROOT_DIR, file_path);
    
    if (!fullPath.startsWith(ROOT_DIR)) {
      return { content: [{ type: "text", text: "エラー: ルートディレクトリ外へのアクセスは禁止" }] };
    }
    
    const content = await fs.readFile(fullPath, "utf-8");
    const lines = content.split("\n").slice(0, max_lines);
    const truncated = content.split("\n").length > max_lines
      ? `\n\n--- (${content.split("\n").length - max_lines}行省略) ---`
      : "";
    
    return {
      content: [{ type: "text", text: lines.join("\n") + truncated }],
    };
  }
);

// ツール3: ファイル内テキスト検索
server.tool(
  "search_files",
  "ファイル内容をキーワード検索し、マッチした行を返す",
  {
    query: z.string().describe("検索キーワード"),
    directory: z.string().optional().default(".").describe("検索ディレクトリ"),
    extension: z.string().optional().describe("対象拡張子"),
  },
  async ({ query, directory, extension }) => {
    const targetDir = path.resolve(ROOT_DIR, directory);
    if (!targetDir.startsWith(ROOT_DIR)) {
      return { content: [{ type: "text", text: "エラー: アクセス禁止" }] };
    }
    
    const entries = await fs.readdir(targetDir, { withFileTypes: true, recursive: true });
    const results: string[] = [];
    
    for (const entry of entries) {
      if (!entry.isFile()) continue;
      const filePath = path.join(entry.parentPath || targetDir, entry.name);
      if (extension && !filePath.endsWith(extension)) continue;
      
      try {
        const content = await fs.readFile(filePath, "utf-8");
        const lines = content.split("\n");
        lines.forEach((line, i) => {
          if (line.toLowerCase().includes(query.toLowerCase())) {
            const relPath = path.relative(ROOT_DIR, filePath);
            results.push(`${relPath}:${i + 1}: ${line.trim()}`);
          }
        });
      } catch { /* バイナリファイル等はスキップ */ }
    }
    
    return {
      content: [{ type: "text", text: results.slice(0, 50).join("\n") || "マッチなし" }],
    };
  }
);

// リソース: プロジェクト情報
server.resource(
  "project-info",
  "project://info",
  async (uri) => ({
    contents: [{
      uri: uri.href,
      mimeType: "text/plain",
      text: `ルートディレクトリ: ${ROOT_DIR}\nサーバー: file-explorer v1.0.0`,
    }],
  })
);

// サーバー起動
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("file-explorer MCPサーバーが起動しました");
}

main().catch(console.error);

ビルドと動作確認

# ビルド
npm run build

# MCP Inspectorで動作確認(公式デバッグツール)
npx @modelcontextprotocol/inspector node ./build/index.js

MCP Inspectorはブラウザベースのデバッグツールで、ツールの一覧確認・テスト実行・レスポンス検証ができる。http://localhost:6274 でUIが開く。

sequenceDiagram participant C as Claude Code participant S as MCPサーバー participant F as ファイルシステム C->>S: initialize(接続開始) S-->>C: capabilities(ツール一覧) C->>S: tools/call "list_files" S->>F: readdir(ディレクトリ読み取り) F-->>S: ファイル一覧 S-->>C: 結果(JSON) C->>S: tools/call "read_file" S->>F: readFile(ファイル読み取り) F-->>S: ファイル内容 S-->>C: 結果(JSON)

実践2:データベース連携MCPサーバーをPythonで構築する

次に、SQLiteデータベースに自然言語でクエリできるMCPサーバーをPythonで作る。「売上トップ10の商品を教えて」とAIに聞くと、このサーバーがSQLクエリを実行して結果を返す。

サーバー実装

import sqlite3
from pathlib import Path
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("db-explorer")

# データベースパス(環境変数 or デフォルト)
DB_PATH = Path(__file__).parent / "data.db"

def get_connection() -> sqlite3.Connection:
    conn = sqlite3.connect(str(DB_PATH))
    conn.row_factory = sqlite3.Row
    return conn

@mcp.tool()
def list_tables() -> str:
    """データベース内の全テーブルとカラム情報を返す"""
    conn = get_connection()
    cursor = conn.execute(
        "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name"
    )
    tables = [row["name"] for row in cursor]
    
    result = []
    for table in tables:
        cols = conn.execute(f"PRAGMA table_info({table})").fetchall()
        col_info = ", ".join(f"{c['name']} ({c['type']})" for c in cols)
        count = conn.execute(f"SELECT COUNT(*) as cnt FROM {table}").fetchone()["cnt"]
        result.append(f"📊 {table} ({count}行): {col_info}")
    
    conn.close()
    return "\n".join(result) if result else "テーブルが見つかりません"

@mcp.tool()
def run_query(sql: str) -> str:
    """SQLクエリを実行して結果を返す(SELECT文のみ)
    
    Args:
        sql: 実行するSELECTクエリ
    """
    # SELECT文のみ許可(安全対策)
    normalized = sql.strip().upper()
    if not normalized.startswith("SELECT"):
        return "エラー: SELECT文のみ実行可能です(安全対策)"
    
    conn = get_connection()
    try:
        cursor = conn.execute(sql)
        rows = cursor.fetchmany(100)  # 最大100行
        
        if not rows:
            return "結果: 0行"
        
        # ヘッダー
        headers = [desc[0] for desc in cursor.description]
        lines = [" | ".join(headers)]
        lines.append("-" * len(lines[0]))
        
        # データ行
        for row in rows:
            lines.append(" | ".join(str(v) for v in row))
        
        total = conn.execute(f"SELECT COUNT(*) FROM ({sql})").fetchone()[0]
        if total > 100:
            lines.append(f"\n(100/{total}行を表示)")
        
        return "\n".join(lines)
    except sqlite3.Error as e:
        return f"SQLエラー: {e}"
    finally:
        conn.close()

@mcp.tool()
def get_sample_data(table_name: str, limit: int = 5) -> str:
    """テーブルのサンプルデータを返す
    
    Args:
        table_name: テーブル名
        limit: 取得行数(デフォルト5)
    """
    conn = get_connection()
    try:
        # テーブル名のバリデーション(SQLインジェクション防止)
        tables = [r["name"] for r in conn.execute(
            "SELECT name FROM sqlite_master WHERE type='table'"
        )]
        if table_name not in tables:
            return f"エラー: テーブル '{table_name}' は存在しません"
        
        cursor = conn.execute(f"SELECT * FROM [{table_name}] LIMIT ?", (limit,))
        rows = cursor.fetchall()
        
        if not rows:
            return "データなし"
        
        headers = [desc[0] for desc in cursor.description]
        lines = [" | ".join(headers)]
        for row in rows:
            lines.append(" | ".join(str(v) for v in row))
        
        return "\n".join(lines)
    finally:
        conn.close()

# リソース: スキーマ情報
@mcp.resource("schema://all")
def get_schema() -> str:
    """全テーブルのCREATE TABLE文を返す"""
    conn = get_connection()
    schemas = conn.execute(
        "SELECT sql FROM sqlite_master WHERE type='table' AND sql IS NOT NULL"
    ).fetchall()
    conn.close()
    return "\n\n".join(row["sql"] for row in schemas)

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

テスト用データベース作成

# テスト用DBを作成
python3 -c "
import sqlite3
conn = sqlite3.connect('data.db')
conn.execute('''CREATE TABLE products (
    id INTEGER PRIMARY KEY, name TEXT, price INTEGER, category TEXT, stock INTEGER
)''')
conn.executemany('INSERT INTO products VALUES (?,?,?,?,?)', [
    (1, 'AIカメラ', 29800, 'ガジェット', 150),
    (2, 'スマートスピーカー', 12800, 'ガジェット', 300),
    (3, 'コーディングキーボード', 19800, 'デバイス', 80),
    (4, 'LLMサーバーGPU', 498000, 'サーバー', 12),
    (5, 'USBマイク', 5980, 'デバイス', 500),
])
conn.commit()
conn.close()
print('テストDB作成完了')
"

# MCP Inspectorで動作確認
mcp dev server.py

実践3:外部API連携MCPサーバーの構築パターン

3つ目のパターンとして、外部REST APIをラップするMCPサーバーを作る。ここでは GitHub APIを叩いて、リポジトリ情報を取得するサーバーを TypeScript で実装する。

#!/usr/bin/env node
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: "github-explorer",
  version: "1.0.0",
});

const GITHUB_TOKEN = process.env.GITHUB_TOKEN || "";

async function githubFetch(endpoint: string) {
  const headers: Record<string, string> = {
    "Accept": "application/vnd.github.v3+json",
    "User-Agent": "mcp-github-explorer",
  };
  if (GITHUB_TOKEN) {
    headers["Authorization"] = `Bearer ${GITHUB_TOKEN}`;
  }
  
  const res = await fetch(`https://api.github.com${endpoint}`, { headers });
  if (!res.ok) throw new Error(`GitHub API error: ${res.status}`);
  return res.json();
}

// ツール: リポジトリ情報取得
server.tool(
  "get_repo",
  "GitHubリポジトリの詳細情報を取得する",
  {
    owner: z.string().describe("リポジトリオーナー"),
    repo: z.string().describe("リポジトリ名"),
  },
  async ({ owner, repo }) => {
    const data = await githubFetch(`/repos/${owner}/${repo}`);
    const info = [
      `📦 ${data.full_name}`,
      `⭐ ${data.stargazers_count.toLocaleString()} stars`,
      `🍴 ${data.forks_count.toLocaleString()} forks`,
      `📝 ${data.description || "説明なし"}`,
      `🔧 言語: ${data.language || "不明"}`,
      `📅 最終更新: ${new Date(data.updated_at).toLocaleDateString("ja-JP")}`,
      `📄 ライセンス: ${data.license?.spdx_id || "不明"}`,
    ].join("\n");
    
    return { content: [{ type: "text", text: info }] };
  }
);

// ツール: リポジトリ検索
server.tool(
  "search_repos",
  "キーワードでGitHubリポジトリを検索する",
  {
    query: z.string().describe("検索キーワード"),
    sort: z.enum(["stars", "forks", "updated"]).optional().default("stars"),
    limit: z.number().optional().default(10),
  },
  async ({ query, sort, limit }) => {
    const data = await githubFetch(
      `/search/repositories?q=${encodeURIComponent(query)}&sort=${sort}&per_page=${limit}`
    );
    
    const results = data.items.map((item: any, i: number) =>
      `${i + 1}. ${item.full_name} (⭐${item.stargazers_count}) - ${item.description || ""}`
    );
    
    return {
      content: [{ type: "text", text: `検索結果 (${data.total_count}件中${limit}件):\n\n${results.join("\n")}` }],
    };
  }
);

// ツール: Issue一覧取得
server.tool(
  "list_issues",
  "リポジトリのオープンIssue一覧を取得する",
  {
    owner: z.string(),
    repo: z.string(),
    limit: z.number().optional().default(10),
  },
  async ({ owner, repo, limit }) => {
    const data = await githubFetch(`/repos/${owner}/${repo}/issues?state=open&per_page=${limit}`);
    
    const issues = data.map((issue: any) =>
      `#${issue.number} ${issue.title} (${issue.labels.map((l: any) => l.name).join(", ") || "ラベルなし"})`
    );
    
    return {
      content: [{ type: "text", text: issues.join("\n") || "オープンIssueなし" }],
    };
  }
);

async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}

main().catch(console.error);

3パターンの使い分け

パターン 通信先 セキュリティ 代表的な用途
ファイル操作 ローカルFS ディレクトリトラバーサル防止 プロジェクト探索、ドキュメント管理
DB連携 SQLite/PostgreSQL SELECT文のみ許可 データ分析、レポート生成
外部API REST/GraphQL APIキー管理、レート制限 GitHub連携、Slack通知

MCPサーバーをAIクライアントに接続する方法

Claude Codeとの接続

Claude Codeは claude mcp add コマンドで簡単にMCPサーバーを登録できる。

# TypeScriptサーバーを登録
claude mcp add file-explorer node /path/to/build/index.js

# 環境変数付きで登録
claude mcp add github-explorer \
  -e GITHUB_TOKEN=ghp_xxxxxxxxxxxx \
  node /path/to/build/index.js

# Pythonサーバーを登録
claude mcp add db-explorer python /path/to/server.py

# 登録済みサーバー一覧
claude mcp list

# サーバー削除
claude mcp remove file-explorer

MCPサーバーの設定ファイルは ~/.claude/claude_desktop_config.json に保存される。手動で編集することも可能だ。

{
  "mcpServers": {
    "file-explorer": {
      "command": "node",
      "args": ["/path/to/build/index.js"],
      "env": {
        "MCP_ROOT_DIR": "/home/user/projects"
      }
    },
    "db-explorer": {
      "command": "python",
      "args": ["/path/to/server.py"]
    }
  }
}

Cursorとの接続

Cursorは .cursor/mcp.json をプロジェクトルートに配置する。AIコーディングツールの選び方については、Claude CodeとCursorの違い比較記事も参照してほしい。

{
  "mcpServers": {
    "file-explorer": {
      "command": "node",
      "args": ["./build/index.js"]
    }
  }
}

VS Code + Copilotとの接続

VS Codeの設定(settings.json)に追加する。

{
  "mcp": {
    "servers": {
      "file-explorer": {
        "command": "node",
        "args": ["./build/index.js"]
      }
    }
  }
}
graph TB subgraph Clients["AIクライアント"] CC["Claude Code
claude mcp add"] CU["Cursor
.cursor/mcp.json"] VS["VS Code Copilot
settings.json"] end subgraph Servers["自作MCPサーバー"] S1["file-explorer
ファイル操作"] S2["db-explorer
DB連携"] S3["github-explorer
API連携"] end CC --> S1 CC --> S2 CC --> S3 CU --> S1 CU --> S3 VS --> S1 style CC fill:#D4A574,color:#000 style CU fill:#4A90D9,color:#fff style VS fill:#007ACC,color:#fff style S1 fill:#50C878,color:#fff style S2 fill:#50C878,color:#fff style S3 fill:#50C878,color:#fff

MCPサーバー開発のセキュリティベストプラクティス

MCPサーバーはAIにシステムリソースへのアクセス権を与えるため、セキュリティ設計が極めて重要だ。AgentSealの調査によると、1,808のMCPサーバーのうち66%に脆弱性が発見され、そのうち43%がシェルインジェクションだった。OWASPもGen AIセキュリティプロジェクトでMCPサーバー開発ガイドを公開している。

入力バリデーション

// ❌ 悪い例:パストラバーサル脆弱性
server.tool("read", { path: z.string() }, async ({ path }) => {
  const content = await fs.readFile(path, "utf-8"); // ../../etc/passwd が読める
  return { content: [{ type: "text", text: content }] };
});

// ✅ 良い例:ルートディレクトリ制限
server.tool("read", { path: z.string() }, async ({ path }) => {
  const fullPath = path.resolve(ROOT_DIR, path);
  if (!fullPath.startsWith(ROOT_DIR)) {
    throw new Error("Access denied");
  }
  const content = await fs.readFile(fullPath, "utf-8");
  return { content: [{ type: "text", text: content }] };
});

セキュリティチェックリスト

チェック項目 対策
パストラバーサル path.resolve() + プレフィックスチェック
SQLインジェクション パラメータバインディング、SELECT文のみ許可
APIキー漏洩 環境変数で管理、レスポンスに含めない
過剰なアクセス 最小権限の原則(読み取り専用等)
レート制限 外部API呼び出しにはスロットリングを実装
大量データ 結果の最大行数を制限

認証とアクセス制御

import os
from functools import wraps

def require_auth(func):
    """環境変数でAPIキーを検証するデコレータ"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        api_key = os.environ.get("MCP_API_KEY")
        if not api_key:
            return "エラー: MCP_API_KEY 環境変数が未設定です"
        return func(*args, **kwargs)
    return wrapper

@mcp.tool()
@require_auth
def sensitive_operation(query: str) -> str:
    """認証が必要な操作"""
    # ...

MCPサーバーのデプロイとSSE(Server-Sent Events)対応

ローカルのstdio接続だけでなく、リモートからHTTP経由でアクセスできるSSEトランスポートも利用可能だ。チーム全体で1つのMCPサーバーを共有する場合に有用。

TypeScript SSEサーバー

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from "express";

const app = express();
const server = new McpServer({ name: "remote-server", version: "1.0.0" });

// ツール定義は同じ
server.tool("ping", {}, async () => ({
  content: [{ type: "text", text: "pong" }],
}));

// SSEエンドポイント
let transport: SSEServerTransport;

app.get("/sse", (req, res) => {
  transport = new SSEServerTransport("/messages", res);
  server.connect(transport);
});

app.post("/messages", (req, res) => {
  transport.handlePostMessage(req, res);
});

app.listen(3001, () => {
  console.log("MCPサーバー起動: http://localhost:3001/sse");
});

Streamable HTTP(2025年3月仕様追加)

MCPプロトコルの最新仕様では、SSEに代わるStreamable HTTPトランスポートが追加された。単一のHTTPエンドポイントでリクエスト・レスポンスを処理でき、ステートレスなデプロイが容易になっている。

import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";

const transport = new StreamableHTTPServerTransport({
  sessionIdGenerator: () => crypto.randomUUID(),
});

app.post("/mcp", async (req, res) => {
  await transport.handleRequest(req, res);
});
トランスポート 接続方式 用途
stdio 標準入出力 ローカル開発、CLI統合
SSE HTTP(Server-Sent Events) リモートサーバー共有
Streamable HTTP HTTP POST/レスポンス 本番デプロイ、ステートレス

公式MCPサーバーの活用と既存エコシステム

自前で作る前に、公式リポジトリ modelcontextprotocol/servers83,000+ Star)に収録されている既存サーバーを確認するのが効率的だ。2026年4月時点で200以上の公式・コミュニティサーバーが登録されている。

主要な公式サーバー

# ファイルシステム操作
npx -y @modelcontextprotocol/server-filesystem /path/to/allowed/dir

# GitHub連携
npx -y @modelcontextprotocol/server-github

# PostgreSQL
npx -y @modelcontextprotocol/server-postgres postgresql://localhost/mydb

# Google Drive
npx -y @modelcontextprotocol/server-gdrive

# Slack
npx -y @modelcontextprotocol/server-slack

サイト上では、Unity開発環境をMCPで拡張する方法や、LINE BotをMCPサーバーとして構築する事例セキュリティ診断MCPサーバーなど、実践的なMCPサーバーの解説記事も公開している。

コミュニティサーバーの探し方

MCP公式のサーバーディレクトリは以下で確認できる。

graph LR subgraph Official["公式サーバー"] FS["filesystem
ファイル操作"] GH["github
GitHub連携"] PG["postgres
DB連携"] GD["gdrive
Google Drive"] end subgraph Community["コミュニティ"] UN["unity-mcp
Unity開発"] LN["line-bot-mcp
LINE Bot"] SC["mcp-for-security
セキュリティ"] TR["tradingview-mcp
トレード"] end subgraph Registry["レジストリ"] SM["Smithery.ai
2,000+サーバー"] AW["awesome-mcp-servers
Star 40K+"] end Official --> Registry Community --> Registry style FS fill:#4A90D9,color:#fff style GH fill:#4A90D9,color:#fff style PG fill:#4A90D9,color:#fff style GD fill:#4A90D9,color:#fff

MCPサーバー開発のトラブルシューティング

開発中によく遭遇するエラーと解決策をまとめる。

よくあるエラーと解決策

エラー 原因 解決策
Server disconnected サーバープロセスがクラッシュ console.error でログ出力。stdoutにデバッグログを出すとJSON-RPCが壊れる
Tool not found ツール名の不一致 MCP Inspectorで登録済みツールを確認
Invalid params Zodスキーマと引数の不一致 スキーマ定義を確認。z.optional() の付け忘れに注意
ENOENT ファイルパスの解決失敗 path.resolve() で絶対パスに変換
EACCES 権限不足 chmod でファイル権限を確認

デバッグのコツ

# MCP Inspectorで対話的にテスト
npx @modelcontextprotocol/inspector node ./build/index.js

# Claude Codeのデバッグログ
claude mcp list  # 登録状況確認

# Pythonサーバーのデバッグ
mcp dev server.py  # 開発モードで起動

重要: MCPサーバーの console.log() はJSON-RPCのstdioストリームに書き込まれるため、デバッグ出力には必ず console.error() を使うこと。

// ❌ stdoutに出力 → JSON-RPCが壊れる
console.log("デバッグ情報");

// ✅ stderrに出力 → 安全
console.error("デバッグ情報");

まとめ:MCPサーバー構築の次のステップ

MCPサーバーの構築は、TypeScript SDKなら McpServer クラスに server.tool() でツールを登録するだけ、Python SDKなら FastMCP@mcp.tool() デコレータを付けるだけで始められる。

本記事で紹介した3パターンを組み合わせることで、AIアシスタントにファイル操作・データベース検索・外部API連携のあらゆる機能を追加できる。

次のステップとして推奨するのは以下の3つだ。

  1. 既存の公式サーバーを使ってみるnpx -y @modelcontextprotocol/server-filesystem から始めるのが最も簡単
  2. MCP Inspectorでデバッグ環境を整える — ブラウザでツールをテストできる
  3. 自分の業務に特化したサーバーを作る — 社内API、独自データベース、プロジェクト管理ツールとの連携

AIエージェント開発の全体像を把握するには、フレームワーク比較記事も参考にしてほしい。MCPサーバーは単体でも強力だが、エージェントフレームワークと組み合わせることで、より複雑なワークフローを構築できる。

参照ソース

よくある質問
MCPサーバーとは何ですか?
Model Context Protocol(MCP)サーバーは、AIアシスタントに外部ツールやデータソースへのアクセスを提供する標準化されたインターフェースです。Claude Code、Cursor、Clineなど対応クライアントから呼び出せます。
MCPサーバーの開発にはどの言語が使えますか?
公式SDKはTypeScript(@modelcontextprotocol/sdk)とPython(mcp)の2つ。TypeScript SDKはnpmで週間15万DL以上、Python SDKはpipで月間50万DL以上の利用実績があります。
MCPサーバーを作るのにどのくらいの時間がかかりますか?
基本的なツール1つのサーバーなら30分〜1時間。ファイル操作やDB連携など実用的なサーバーでも2〜3時間で動くものが作れます。
MCPとREST APIの違いは何ですか?
REST APIはHTTP経由の汎用インターフェース。MCPはAIモデルとツールの接続に特化したプロトコルで、ツール定義・リソース公開・プロンプト管理の3機能を標準化。AIクライアントが自動的にツールを発見・呼び出しできます。
自作MCPサーバーをClaude Codeで使うには?
claude mcp add コマンドでサーバーを登録します。例:claude mcp add my-server node ./build/index.js。登録後はClaude Codeが自動的にツールを認識し、必要に応じて呼び出します。
広告
GitHub で見る X 🧵 Threads Facebook LINE B! はてブ
🔔 AI速報、毎日Xで配信中
Claude Code・MCP・AIエージェントの最新ニュースをいち早くお届け
@peaks2314 をフォロー
記事の信頼性について
AI Heartland エディトリアルポリシーに基づき作成
複数ソース照合
公式情報・報道等を突き合わせて確認
ファクトチェック済
ソースURLの内容を検証
参照ソース明記
記事末尾に引用元を掲載
Next Read →
🤖 AIエージェントフレームワーク比較2026年版:LangGraph・CrewAI・Dify・OpenHands含む9種徹底解説
関連記事
⚔️ Claude Code vs Cursor徹底比較2026年版:CLI派とIDE派、どちらを選ぶべきか
Claude CodeとCursorを14項目で徹底比較。料金・AI性能・MCP対応・拡張性・ワークフローの違いを実データで解説。CLI派のClaude CodeとIDE派のCursor、2026年版の最適解がプロジェクト別に分かる完全ガイド。
2026.04.07
🤖 AIエージェントフレームワーク比較2026年版:LangGraph・CrewAI・Dify・OpenHands含む9種徹底解説
2026年版AIエージェントフレームワーク9種を徹底比較。LangGraph・CrewAI・Dify・OpenHands・Claude Agent SDK・PydanticAI・Google ADK・Mastra・MS Agent Frameworkのコード例・Star数・選定基準を実データで解説。
2026.04.07
🧭 ダリオ・アモデイ「テクノロジーの思春期」全解説:AIが消す仕事・残す仕事の判断軸
AnthropicCEOダリオ・アモデイが発表した論考「テクノロジーの思春期」を全解説。ホワイトカラー雇用の半数消失予測、5つのAIリスクカテゴリ、キャリア判断に使えるフレームワークを詳述。
2026.04.06
🔗 Claude Microsoft 365 連携ガイド:SharePoint・Outlook・Teams接続と活用例
ClaudeのMicrosoft 365コネクタを使えばSharePoint・OneDrive・Outlook・Teamsのデータを横断検索・分析できます。全プラン(Free含む)対応。設定手順・活用例・セキュリティ設定・よくあるトラブル対処を初心者向けに解説します。
2026.04.05
Popular
#1 POPULAR
🔓 Claude Codeのソースコード流出、npmソースマップに51万行が丸見えだった件
Anthropic Claude Codeのnpmパッケージにソースマップが含まれ、1,902ファイル・51万行超のTypeScriptソースが公開状態に。未公開プロジェクト「KAIROS」や107個のフィーチャーフラグなど、内部コードの全貌を解説する。
#2 POPULAR
🚨 【速報】JavaScript主流ライブラリAxios、NPM供給チェーン攻撃でRAT配布
JavaScriptの週間1億DL HTTPクライアント「Axios」がNPM供給チェーン攻撃の被害に。[email protected]と0.30.4に悪意あるパッケージplain-crypto-jsが注入され、クロスプラットフォーム対応RATが配布。証拠自動削除機能を備えた高度な攻撃。
#3 POPULAR
⚠️ Anthropic、Claude Codeで予想外の高速クォータ枯渇認める。キャッシュバグで料金10〜20倍
Claude Codeでプロンプトキャッシュを破壊する2つのバグが発見され、API利用料が10〜20倍に跳ね上がる問題が発生。Anthropicは「チームの最優先事項」と認める。Pro/Maxユーザーから月間の大半で使用不可との報告多数。
#4 POPULAR
🔍 Claude Codeセキュリティ事件を切り分ける:ソース漏洩とaxios攻撃の違いと対処法
3月31日にClaude Codeで起きたソース漏洩とaxiosマルウェア。感染チェックコマンド・対策コードを交えて、2つの別事件の実態と具体的な対応手順を解説。
#5 POPULAR
🚀 ソフトウェア開発者ではない人が400ドルから年7M達成。AI時代の先発者優位性
AI技術を活用して短期間で大規模な収益を生み出した事例から、開発経験がなくても可能な起業の実態と、AI知識の先発者優位性について解説する。
← OpenMontage:Claude CodeをAI動画スタジオに変えるOSSツールの使い方 AIエージェントフレームワーク比較2026年版:LangGraph・CrewAI・Dify・OpenHands含む9種徹底解説 →